home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
Source
/
TCL Terminalpane 1.0
/
CTerminalPane.cp
next >
Wrap
Text File
|
1993-08-16
|
14KB
|
641 lines
/*
** CTerminalPane.h
**
** Eric’s standard libraries
** Terminal display pane
**
** Copyright © 1993, FrostByte Design / Eric Scouten
** Portions copyright © 1990 Symantec Corporation. All rights reserved.
*/
#include "CTerminalPane.h"
#include <Events.h>
#include <LongCoordinates.h>
#include <LongQD.h>
/* global TCL objects */
extern CBureaucrat *gGopher;
/*______________________________________________________________________
**
** ITerminalPane
**
** Initialize the pane. Nothing special here. Parameters are all the same as
** for CPanorama::IPanorama.
**
*/
void CTerminalPane::ITerminalPane (CView *anEnclosure, CBureaucrat *aSupervisor,
short aWidth, short aHeight, short aHEncl, short aVEncl,
SizingOption aHSizing, SizingOption aVSizing)
{
LongRect theBounds;
CPanorama::IPanorama(anEnclosure, aSupervisor, aWidth, aHeight, aHEncl, aVEncl,
aHSizing, aVSizing);
SetLongRect(&theBounds, 0, 0, sizeX, sizeY);
SetBounds(&theBounds);
SetCanBeGopher(TRUE);
SetWantsClicks(TRUE);
if (member(itsEnclosure, CScrollPane))
((CScrollPane *) itsEnclosure)->SetSteps(pixelsX, pixelsY);
blinkCursor = FALSE;
cursorVis = TRUE;
lastCursorCol = lastCursorLine = 0;
lastCursorTick = 0L;
DoClearScreen();
}
/*______________________________________________________________________
**
** BecomeGopher
**
** Become the gopher (or leave gopher status). All our routine does is force a refresh
** of the cursor.
**
** fBecoming (Boolean): TRUE if becoming gopher
**
** return (Boolean): TRUE if successful in changing status
**
*/
Boolean CTerminalPane::BecomeGopher (Boolean fBecoming)
{
if (!fBecoming)
cursorVis = TRUE;
InvalCharRect(theColumn, theLine, theColumn, theLine);
return CPanorama::BecomeGopher(fBecoming);
}
/*______________________________________________________________________
**
** CalcCharRect (protected method)
**
** Calculate the screen coordinates for a specified rectangle of
** character coordinates. Upper left corner is (0,0).
**
** left (short): left edge of char rectangle
** top (short): top edge
** right (short): right edge, inclusize
** bottom (short): bottom edge, inclusive
** theRect (LongRect *): coordinates returned to this rectangle
**
*/
void CTerminalPane::CalcCharRect (short left, short top, short right, short bottom,
LongRect *theRect)
{
SetLongRect(theRect,
(long) left * pixelsX + offsetX,
(long) top * pixelsY + offsetY,
(long) (right+1) * pixelsX + offsetX,
(long) (bottom+1) * pixelsY + offsetY);
}
/*______________________________________________________________________
**
** ClearToEOL (protected method)
**
** Clear from the indicated point to the end of a line. Upper left
** corner is (0,0).
**
** col (short): first column in line to clear
** line (short): line to be cleared
**
*/
void CTerminalPane::ClearToEOL (short col, short line)
{
InvalCharRect(col, line, maxX-1, line);
while (col < maxX)
theScreen[line][col++] = ' ';
}
/*______________________________________________________________________
**
** ClearToEOS (protected method)
**
** Clear from the indicated point to the end of the screen. Upper left
** corner is (0,0).
**
** col (short): first column in first line to clear
** line (short): first line to be cleared
**
*/
void CTerminalPane::ClearToEOS (short col, short line)
{
while (line < maxY) {
ClearToEOL(col, line++);
col = 0;
}
}
/*______________________________________________________________________
**
** CursorMoved
**
** Update the blinking cursor block.
**
*/
void CTerminalPane::CursorMoved (void)
{
cursorVis = TRUE;
lastCursorTick = Ticks;
InvalCharRect(lastCursorCol, lastCursorLine, lastCursorCol, lastCursorLine);
InvalCharRect(theColumn, theLine, theColumn, theLine);
lastCursorCol = theColumn;
lastCursorLine = theLine;
}
/*______________________________________________________________________
**
** Dawdle
**
** Blink the cursor if necessary. Cursor blinks at the user-defined rate for insertion point
** blinking.
**
** maxSleep (long *): maximum sleep value, updated if necessary
**
*/
void CTerminalPane::Dawdle (long *maxSleep)
{
CPanorama::Dawdle(maxSleep);
if (blinkCursor) {
*maxSleep = Min(*maxSleep, GetCaretTime());
if (Ticks >= lastCursorTick + GetCaretTime()) {
cursorVis = !cursorVis;
lastCursorTick = Ticks;
InvalCharRect(theColumn, theLine, theColumn, theLine);
}
}
}
/*______________________________________________________________________
**
** DoClearScreen
**
** Clear the screen and move the cursor to (0,0).
**
*/
void CTerminalPane::DoClearScreen (void)
{
register short x, y;
theLine = theColumn = 0;
CursorMoved();
for (y = 0; y < maxY; ++y)
for (x = 0; x < maxX; ++x)
theScreen[y][x] = ' ';
Refresh();
}
/*______________________________________________________________________
**
** DoEraseChar
**
** Erase one character from screen (backspace).
**
*/
void CTerminalPane::DoEraseChar (void)
{
register short x;
if (theColumn > 0) {
theColumn--;
for (x = theColumn; x < maxX-1; x++)
theScreen[theLine][x] = theScreen[theLine][x+1];
theScreen[theLine][maxX-1] = ' ';
InvalCharRect(theColumn, theLine, maxX-1, theLine);
CursorMoved();
}
}
/*______________________________________________________________________
**
** DoEraseLine
**
** Move the cursor back to beginning of line & erase line.
**
*/
void CTerminalPane::DoEraseLine (void)
{
theColumn = 0;
ClearToEOL(0, theLine);
CursorMoved();
}
/*______________________________________________________________________
**
** DoWriteBfr
**
** Write the contents of a text buffer to the terminal. This method
** merely dishes out the characters to the DoWriteChar method.
**
** theStr (char *): the buffer to write
** theSize (short): size of the data buffer
**
*/
void CTerminalPane::DoWriteBfr (char *theStr, short theSize)
{
while (theSize--)
DoWriteChar(*(theStr++));
}
/*______________________________________________________________________
**
** DoWriteChar
**
** Write a character to the terminal. This method handles *basic* terminal emulation.
** To provide more sophisticated emulation, override this method.
**
** theChar (char): the character to write
**
*/
void CTerminalPane::DoWriteChar (char theChar)
{
/* parse a few control characters */
switch (theChar) {
case charNUL:
break;
case charBEL:
SysBeep(0);
break;
case charBS:
if (theColumn > 0)
theColumn--;
CursorMoved();
break;
case charHT:
theColumn = ((short) ((theColumn + 7) / 8)) * 8;
if (theColumn >= maxX)
theColumn = maxX-1;
CursorMoved();
break;
case charLF:
if (theLine < maxY-1)
theLine++;
else
ScrollTerm();
CursorMoved();
ScrollToSelection();
break;
case charFF:
DoClearScreen();
ScrollToSelection();
break;
case charCR:
theColumn = 0;
CursorMoved();
break;
default:
theScreen[theLine][theColumn] = theChar;
InvalCharRect(theColumn, theLine, theColumn, theLine);
if (theColumn < maxX)
theColumn++;
CursorMoved();
}
}
/*______________________________________________________________________
**
** DoWriteCharNum
**
** Write a character number to the terminal. Provided as a debugging routine
** by other classes. The number is bracketed by the two characters indicated, i.e.
** if you call ::DoWriteCharNum('!', '[', ']'), you get [33] written to the terminal.
**
** theChar (char): the character number to write
** leftBracket (char): prefix to character number
** rightBracket(char): suffix to character number
**
*/
void CTerminalPane::DoWriteCharNum (char theChar, char leftBracket, char rightBracket)
{
Str255 cNumber;
DoWriteChar(leftBracket);
NumToString((short) theChar, cNumber);
cNumber[cNumber[0]+1] = '\0';
DoWriteStr((char *) (&cNumber)+1);
DoWriteChar(rightBracket);
}
/*______________________________________________________________________
**
** DoWriteStr
**
** Write a string to the terminal. This method is optimized to handle text strings. It skips
** any control characters and sends them directly to DoWriteChar.
**
** theStr (char *): the string to write (C string)
**
*/
void CTerminalPane::DoWriteStr (char *theStr)
{
short leftCol;
/* optimize for text characters */
while (*theStr) {
if (*theStr >= ' ') {
leftCol = theColumn;
while (*theStr >= ' ') {
theScreen[theLine][theColumn] = *(theStr++);
if (theColumn < maxX)
theColumn++;
}
InvalCharRect(leftCol, theLine, theColumn, theLine);
CursorMoved();
}
if (*theStr)
DoWriteChar(*(theStr++));
}
}
/*______________________________________________________________________
**
** Draw
**
** Draw characters from the theScreen array onto the real screen.
**
** area (Rect *): area to be redrawn (in frame coordinates)
**
*/
void CTerminalPane::Draw (Rect *area)
{
short left, top, right, bottom; /* char coordinatates of draw region */
short dLine; /* where to draw now */
LongRect theLongArea; /* frame coordinates of region */
LongPt theLongPt;
Point thePoint;
/* figure draw region */
QDToFrameR(area, &theLongArea);
left = (theLongArea.left - offsetX) / pixelsX;
right = (theLongArea.right -offsetX + 1) / pixelsX;
top = (theLongArea.top - offsetY) / pixelsY;
bottom = (theLongArea.bottom -offsetY + 1) / pixelsY;
/* do range checking */
left = Max(left, 0);
top = Max(top, 0);
right = Min(right, maxX-1);
bottom = Min(bottom, maxY-1);
/* hard-wire for Monaco 9: Mom, don’t look at this code! */
TextFont(4);
TextFace(0);
TextSize(9);
/* draw the stuff */
if (left <= right) {
HLock((Handle) this);
dLine = top;
while (dLine <= bottom) {
SetLongPt(&theLongPt, left * pixelsX, dLine * pixelsY);
FrameToQD(&theLongPt, &thePoint);
MoveTo(thePoint.h + offsetX, thePoint.v + offsetY + pixelsY-2);
DrawText(&theScreen[dLine][left], 0, right-left+1);
dLine++;
}
HUnlock((Handle) this);
}
/* check to see if we overwrote the cursor */
if ((left <= theColumn) && (right >= theColumn) &&
(top <= theLine) && (bottom >= theLine) && cursorVis)
InvertCursor(theColumn, theLine);
}
/*______________________________________________________________________
**
** InvalCharRect (protected method)
**
** Invalidate a section of the pane based on the character coordinates
** provided. Upper left corner is (0,0).
**
** left (short): left edge of character rectangle
** top (short): top edge
** right (short): right edge, inclusive
** bottom (short): bottom edge, inclusive
**
*/
void CTerminalPane::InvalCharRect (short left, short top, short right, short bottom)
{
LongRect theLongRect;
CalcCharRect(left, top, right, bottom, &theLongRect);
Prepare();
RefreshLongRect(&theLongRect);
}
/*______________________________________________________________________
**
** InvertCursor
**
** Invert a single character.
**
** col (short): the column number
** line (short): the line number
**
*/
void CTerminalPane::InvertCursor (short col, short line)
{
LongRect theLongRect;
Rect theRect;
CalcCharRect(col, line, col, line, &theLongRect);
LongToQDRect(&theLongRect, &theRect);
InvertRect(&theRect);
if (gGopher != this) {
InsetRect(&theRect, 1, 1);
InvertRect(&theRect);
}
}
/*______________________________________________________________________
**
** ScrollTerm
**
** Push everything on screen up one line.
**
*/
void CTerminalPane::ScrollTerm (void)
{
BlockMove(&theScreen[1][0], &theScreen[0][0], (maxY-1) * maxX);
ClearToEOL(0, maxY-1);
Refresh();
}
/*______________________________________________________________________
**
** ScrollToSelection
**
** Ensure that the current cursor location is visible.
**
*/
void CTerminalPane::ScrollToSelection (void)
{
LongRect theVisRect;
LongRect theCharRect;
LongRect tempRect;
LongPt whereTo;
Boolean needsScroll = FALSE;
/* figure out panorama coordinates of what’s visible...
this SHOULD be easier (hint, hint, Symantec) */
GetPosition(&whereTo);
GetBounds(&tempRect);
if (!(PtInLongRect(&whereTo, &tempRect))) {
whereTo.h = 0; /* scroll position is just plain bonkers */
whereTo.v = 0;
needsScroll = TRUE;
}
GetAperture(&tempRect);
theVisRect.left = whereTo.h;
theVisRect.top = whereTo.v;
theVisRect.right = whereTo.h + (tempRect.right - tempRect.left);
theVisRect.bottom = whereTo.v + (tempRect.bottom - tempRect.top);
/* figure out where our cursor is */
CalcCharRect(theColumn, theLine, theColumn, theLine, &theCharRect);
theCharRect.left -= offsetX;
theCharRect.top -= offsetY;
theCharRect.right += offsetX;
theCharRect.bottom += offsetY;
/* scroll if necessary */
if (theCharRect.bottom > theVisRect.bottom) {
whereTo.v = Max(theCharRect.bottom, theVisRect.bottom) -
(theVisRect.bottom - theVisRect.top);
needsScroll = TRUE;
}
if (theCharRect.top < theVisRect.top) {
whereTo.v = Min(Min(theCharRect.top, theVisRect.top), 0);
needsScroll = TRUE;
}
if (theCharRect.right > theVisRect.right) {
whereTo.h = Max(theCharRect.right, theVisRect.right) -
(theVisRect.right - theVisRect.left);
needsScroll = TRUE;
}
if (theCharRect.left < theVisRect.left) {
whereTo.h = Min(Min(theCharRect.left, theVisRect.left), 0);
needsScroll = TRUE;
}
if (needsScroll) {
ScrollTo(&whereTo, FALSE);
Refresh();
}
}
/*______________________________________________________________________
**
** SetBlinking
**
** Turns on or off cursor blinking.
**
** blinkMode (Boolean): TRUE to enable cursor blinking
**
*/
void CTerminalPane::SetBlinking (Boolean blinkMode)
{
blinkCursor = blinkMode;
if ((blinkMode) && (gGopher == this)) {
lastCursorCol = theColumn;
lastCursorLine = theLine;
CursorMoved();
}
else {
cursorVis = TRUE;
InvalCharRect(theColumn, theLine, theColumn, theLine);
}
}